/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2019/5/29 21:01
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.aurora.core.bean;

import com.hyacinth.aurora.core.exception.BeansException;
import com.hyacinth.aurora.utils.ArrayUtil;
import com.hyacinth.aurora.utils.Assert;
import com.hyacinth.aurora.utils.ClassUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 默认bean工厂
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class AuroraBeanFactory implements BeanFactory {

    /** bean容器 **/
    private final Map<String, Object> beans = new ConcurrentHashMap<>();

    @Override
    public void register(Object object) {
        Assert.notNull(object, "注册的bean实例为空");
        register(object.getClass().getName(), object);
    }

    @Override
    public void register(String beanName, Object object) {
        Assert.notBlank(beanName, "注册的beanName为空");
        Assert.notNull(object, "注册的bean实例为空");
        beans.put(beanName, object);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        String[] beanNames = getBeanNamesForType(requiredType);
        if (beanNames.length == 1) {
            return getBean(beanNames[0], requiredType);
        } else if (beanNames.length > 1) {
            throw new BeansException("expected single matching bean but found " + beanNames.length + " : " + ArrayUtil.toString(beanNames));
        } else {
            throw new BeansException("No qualifying bean of type '" + ClassUtil.getQualifiedName(requiredType) + "' available");
        }
    }

    /**
     * 获取类对应的bean名称集合
     * @param requiredType
     * @param <T>
     * @return
     */
    private <T> String[] getBeanNamesForType(Class<T> requiredType) {
        List<String> matches = new ArrayList<>();
        for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
            String name = entry.getKey();
            Object beanInstance = entry.getValue();
            if (requiredType.isInstance(beanInstance)) {
                matches.add(name);
            }
        }
        return matches.toArray(new String[0]);
    }

    @Override
    public Object getBean(String name) throws BeansException {
        String beanName = BeanFactoryUtils.transformedBeanName(name);
        Object bean = beans.get(beanName);
        if (bean == null) {
            throw new BeansException("No bean named '" + name + "' available");
        }
        return bean;
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        Object bean = getBean(name);
        if (requiredType != null && !requiredType.isInstance(bean)) {
            String errMsg = "Bean named '" + name + "' is expected to be of type '" + ClassUtil.getQualifiedName(requiredType) +
                    "' but was actually of type '" + ClassUtil.getQualifiedName(bean.getClass()) + "'";
            throw new BeansException(errMsg);
        }
        return (T) bean;
    }

    @Override
    public void releaseBean() {
        beans.clear();
    }

    @Override
    public Map<String, Object> getBeanMap() {
        return beans;
    }
}
